iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0
JavaScript

Don't make JavaScript Just Surpise系列 第 22 篇

代理物件(Proxy)

  • 分享至 

  • xImage
  •  

Proxy 是一個 ES 6 引入的新物件,Proxy 一詞在中文中是「代理」的意思。

現實中的例子來舉例,比如老闆管理一家公司,日理萬機,可不是誰都能見到老闆的。
那由誰來決定哪些人可以見老闆?秘書這個職位就負責處理這些事情。秘書熟知老闆的行程,做事習慣,手上的工作等等,透過瞭解老闆的優先序,他能為老闆排出最適切的會面對象。

秘書就是 Proxy。
秘書是一個中間人的角色,他並不能直接改變老闆,但可以對要到達老闆前的事情做一層攔截跟處理。

Proxy 在 JS 的宣告方式如下

const p = new Proxy(target, handler)

兩個參數分別是 target 和 handler,target 用於傳入原始對象(要代理的物件,老闆),handler 用於定義代理的處理行為。

handler

JS 在對物件的操作上,多有定義內部方法(Internal Methods)來處理,這些方法我們沒辦法透過方法的名稱來使用,而是透過對應規範的語法來使用。
如 obj.name 實際上是對 obj 呼叫了內部方法 [[Get]],傳入屬性名稱並期望獲得物件上對應屬性的值。

一般來說內部方法會用 [[]] 來表示,由於不可呼叫性,如果對內部方法的實踐有興趣,就需要直接查閱引擎的原始碼(如 V8),才能了解如何實踐的,或是閱讀 ECMAScript,會有對應的規範描述。
我們通常會更熟知表面的行為如何反應而不去關注過多的內部實踐。

提到內部方法是因為 handler 主要的攔截對象就是對目標物件的操作。
handler 用於攔截的方法包括以下幾種,都對應到各種內部方法。

這些攔截方法被稱為攔截器(trap),當對目標物件用了對應的的方法,handler 就會被觸發且處理:

  1. apply:攔截 apply()
  2. construct:攔截 new
  3. defineProperty(), deleteProperty, getOwnPropertyDescriptor():攔截對應對屬性的操作
  4. has(), ownKeys(), :對應到攔截 in,ownKeys() 的操作
  5. get(), set():攔截對物件上屬性的讀取或寫入操作
  6. isExtensible(), preventExtensions():攔截對物件的原型鏈繼承的操作
  7. setPrototypeOf() :攔截對物件原型的操作

來看一個實際的例子:

//明星
const star = {
    name: 'Big Star',
    address: 'Moon Street',
    phone: '987-654-3210',
    nextConcert: '2024-10-06',
    getNextConcert() {
        return this.nextConcert;
    }
};

//經紀人
const agentHandler = {
//攔截對明星資訊的訪問
    get(target, property) {
        //只有當詢問演唱會時間才會回答
        if (property === 'nextConcert') {
            return target.getNextConcert();ㄥ
        } else {
            //其他一概不知道
            console.log(`Proxy get the request to : ${property} and block.`);
            return undefined;
        }
    }
};

//使用 Proxy 創建經紀人
const agent = new Proxy(star, agentHandler);

console.log(agent.nextConcert); // "2024-10-06"
console.log(agent.address); //"Proxy get the request to : address and block." undefined
console.log(agent.phone); // "Proxy get the request to : phone and block." undefined

代理使用例子

代理的攔截一般可以作用於幾種場景:

  1. 驗證傳入的資料或格式化:攔截 set,如果出現非預期的資料可以調整後再 set,或拋出錯誤。
  2. 設定預設值:攔截 construct,為建構參數塞入額外的東西,或是攔截 get 在值不存在的情況下返回特定值。

Proxy 其中一個有名的實踐就是 Vue.js(這裡指 Vue 3 以後的版本)。
有使用過 Vue.js 的人應該知道,Vue.js 是一套 JS 的框架,主要用於前端開發,MVVM 的架構讓開發者可以專注在操作後端的資料流,前端的元件就自動對應反應。

「改後端的資料,讓前端的元件自動對應反應」,聽起來是不是就像是代理的行為一樣?對,因為 Vue.js 就是為每個資料都建立對應的 Proxy 物件,攔截 get 與 set 行為,這樣一旦資料更新,他便能對對應的 DOM元件 或 JS物件,做定義的操作,從而達到響應式的畫面與程式。

Proxy 在對物件的監視、同步、攔截上都有很好效果,希望這篇能讓大家更熟悉這個 Proxy 的特性與使用範例。


上一篇
例外(Exception)、錯誤物件(Error)與攔截
下一篇
反射(Reflect)
系列文
Don't make JavaScript Just Surpise 共 31 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言